package org.qatools.validator;

import java.util.LinkedList;
import java.util.List;


/**
 * <pre>
 * Validator extends Junit by capturing and reporting all failures all at once.
 * This sppeds your QA effort by reducing the number of iterations.
 * 
 * Junit differs because it pukes at the first failed assertion.
 * 
 * 
 * Junit example - notice junit does not force useful error messages
 * 			assertTrue(false);		<=== FAILS IMMEDIATELY
 * 			assertEquals(1, 2)		
 * 			assertNotNull(null);	
 * 
 * Validator example - the API forces you to enter useful error messages
 * 			Validator v = new Validator();
 * 			v.assertTrue("Foo msg", false);
 * 			v.assertEquals("Bar msg", 1, 2);
 * 			v.assertNotNull("Apple", null);
 * 			v.junitAssertNoErrors();		<=== Reports everything at once!
 * 
 * Unit test showing sample usage:
 * 			http://bit.ly/Lc9sue
 * </pre>
 * 
 * @author scottwilson
 */
public class Validator  {

    
    // ------------------------------------------------------
    // ----- class variables
    // ------------------------------------------------------

    //errors
    private List<String> errors = null;

    
    // ------------------------------------------------------
    // ----- constructors
    // ------------------------------------------------------

    public Validator() {
        //do nothing constructor
    }
    

    // ------------------------------------------------------
    // ----- Custom code
    // ------------------------------------------------------
    
    /**
     * Adds an error to the error list
     * 
     * @param error
     */
    public void addError(String error) {
    	if (errors == null) {
    		errors = new LinkedList<String>();
    	}
    	errors.add(error);
    }
    
    
    /**
     * Check if there are any errors
     * 
     * @return
     */
    public boolean doErrorsExist() {
    	if (errors==null || errors.size()==0) {
    		return false;
    	}
    	return true;
    }
    
    /**
     * If any errors where captured, throw an Exception
     */
    public void junitAssertNoErrors() throws Exception {
    	if (doErrorsExist()) {
    		StringBuffer sb = new StringBuffer();
    		for (String str : errors) {
    			sb.append("\n" + str);
    		}
    		sb.append("\n");
    		throw new Exception(sb.toString());
    	}    	
    }
    
    /**
     * Returns the list of errors
     * 
     * @return
     */
    public List<String> getErrors() {
    	return errors;
    }
    
    // ------------------------------------------------------
    // ----- Re-implements these Junit APIs
    // ------------------------------------------------------

    
	/**
	 * Asserts that a condition is true. If it isn't it adds the error to the error list..
	 */
	public void assertTrue(String message, boolean condition) {
		if (!condition) {
			addError(message);
		}
	}
	

	
	/**
	 * Asserts that a condition is false. If it isn't it adds the error to the error list.
	 */
	public void assertFalse(String message, boolean condition) {
		assertTrue(message, !condition);
	}
	


	/**
	 * Adds the message to the error list
	 */
	public void fail(String message) {
		addError(message);
	}



	/**
	 * Asserts that two objects are equal. If they are not
	 * add the message to the error list
	 */
	public void assertEquals(String message, Object expected, Object actual) {
		if (expected == null && actual == null)
			return;
		if (expected != null && expected.equals(actual))
			return;
		failNotEquals(message, expected, actual);
	}


	/**
	 * Asserts that two Strings are equal. 
	 */
	public void assertEquals(String message, String expected, String actual) {
		if (expected == null && actual == null)
			return;
		if (expected != null && expected.equals(actual))
			return;
		failNotEquals(message, expected, actual);
	}


	
	/**
	 * Asserts that two doubles are equal concerning a delta.  If the expected
	 * value is infinity then the delta value is ignored.
	 */
	public void assertEquals(String message, double expected, double actual, double delta) {
		// handle infinity specially since subtracting to infinite values gives NaN and the
		// the following test fails
		if (Double.isInfinite(expected)) {
			if (!(expected == actual))
				failNotEquals(message, new Double(expected), new Double(actual));
		} else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
			failNotEquals(message, new Double(expected), new Double(actual));
	}
	


	/**
	 * Asserts that two floats are equal concerning a delta. If they are not
	 * an AssertionFailedError is thrown with the given message.  If the expected
	 * value is infinity then the delta value is ignored.
	 */
	public void assertEquals(String message, float expected, float actual, float delta) {
 		// handle infinity specially since subtracting to infinite values gives NaN and the
		// the following test fails
		if (Float.isInfinite(expected)) {
			if (!(expected == actual))
				failNotEquals(message, new Float(expected), new Float(actual));
		} else if (!(Math.abs(expected-actual) <= delta))
      		failNotEquals(message, new Float(expected), new Float(actual));
	}

	
	/**
	 * Asserts that two longs are equal. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 */
	public void assertEquals(String message, long expected, long actual) {
	    assertEquals(message, new Long(expected), new Long(actual));
	}
	
	/**
	 * Asserts that two booleans are equal. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 */
	public void assertEquals(String message, boolean expected, boolean actual) {
    		assertEquals(message, new Boolean(expected), new Boolean(actual));
  	}
	
	/**
	 * Asserts that two bytes are equal. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 */
  	public void assertEquals(String message, byte expected, byte actual) {
		assertEquals(message, new Byte(expected), new Byte(actual));
	}

  	
	/**
	 * Asserts that two chars are equal. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 */
  	public void assertEquals(String message, char expected, char actual) {
    		assertEquals(message, new Character(expected), new Character(actual));
  	}

	/**
	 * Asserts that two shorts are equal. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 */
	public void assertEquals(String message, short expected, short actual) {
    		assertEquals(message, new Short(expected), new Short(actual));
	}

	/**
	 * Asserts that two ints are equal. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 */
  	public void assertEquals(String message, int expected, int actual) {
		assertEquals(message, new Integer(expected), new Integer(actual));
  	}

	/**
	 * Asserts that an object isn't null. 
	 */
	public void assertNotNull(String message, Object object) {
		assertTrue(message, object != null);
	}
	
	/**
	 * Asserts that an object is null. 
	 */
	public void assertNull(String message, Object object) {
		assertTrue(message, object == null);
	}
	
	/**
	 * Asserts that two objects refer to the same object. 
	 */
	public void assertSame(String message, Object expected, Object actual) {
		if (expected == actual)
			return;
		failNotSame(message, expected, actual);
	}

 	/**
 	 * Asserts that two objects DO NOT refer to the same object. 
 	 */
	public void assertNotSame(String message, Object expected, Object actual) {
		if (expected == actual)
			failSame(message);
	}
	

	private void failSame(String message) {
		String formatted= "";
 		if (message != null)
 			formatted= message+" ";
 		fail(formatted+"expected not same");
	}

	private void failNotSame(String message, Object expected, Object actual) {
		String formatted= "";
		if (message != null)
			formatted= message+" ";
		fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
	}

	private void failNotEquals(String message, Object expected, Object actual) {
		fail(format(message, expected, actual));
	}

	String format(String message, Object expected, Object actual) {
		String formatted= "";
		if (message != null)
			formatted= message+" ";
		return formatted+"expected:<"+expected+"> but was:<"+actual+">";
	}

	
	

    
}
